
#pragma once
#include "GameTime.h"
#include <vector>
#include <thread>
#include <mutex>
#include <queue>
#include <condition_variable>
#include <atomic>
#include <memory>

namespace FlameEngine
{
	class CoreEngine;
	class IRenderWindow;
	class IEngineCommand;

	class RenderEngine
	{
	public:
		RenderEngine(IRenderWindow* renderWnd);
		~RenderEngine();

		void Init(CoreEngine* pCoreEngine);

		//start render engine, should only be called once
		void StartRun();

		//Send command to render engint. TSM
		inline void SendEngineCmd(IEngineCommand* pCmd);
		
		//Get render engine current frame count. TSM
		inline uint32_t GetFrameCount();

		//
		inline CoreEngine* GetCoreEngine();

		//
		inline IRenderWindow* GetRenderWnd();

		using EngineCmdCallback = std::function <void(IEngineCommand*)>;
		void RegisterCmdCallback(uint16_t cmdType, EngineCmdCallback&);

		void UnregisterCmdCallback(uint16_t cmdType);

	protected:
		void ProcessEngineCmd(IEngineCommand* pCmd);

		//CreateWndCmd command callback
		void CreateWnd(IEngineCommand* pCmd);
	private:
		IRenderWindow* m_pRenderWnd;
		CoreEngine* m_pCoreEngine;
		bool m_inited;

		std::atomic<uint32_t> m_frameCount;

		std::mutex m_renderThreadMutex;
		std::condition_variable m_renderThreadController;
		
		std::mutex m_engineCmdMtx;
		std::queue<IEngineCommand*> m_engineCmd;
		std::vector<EngineCmdCallback> m_cmdCallbacks;
	};

	inline CoreEngine* RenderEngine::GetCoreEngine() { return m_pCoreEngine; }

	inline void RenderEngine::SendEngineCmd(IEngineCommand* pCmd)
	{
		std::lock_guard<std::mutex> lock(m_engineCmdMtx);
		m_engineCmd.push(pCmd);
		if(m_engineCmd.size()==1)
			m_renderThreadController.notify_one();
	}

	inline uint32_t RenderEngine::GetFrameCount(){ return m_frameCount.load(); }

	inline IRenderWindow* RenderEngine::GetRenderWnd(){ return m_pRenderWnd; }

}